/*
 * sample C
 * syntax analysis with error recovery
 * symbol table
 * memory allocation
 * (shift/reduce conflics: one on ELSE
 */

%{
   #include <stdio.h>
   #include <stdlib.h>
   FILE *yyerfp;             /* puntero al fichero de errores */
   enum op { LOAD = 0, LOADA, LOADI, LOADL, STORE, STOREI, CALL,
          CALLI, RETURN, PUSH, POP, JUMP, JUMPI, JUMPIF, HALT };

   enum r { CB = 0, CT, PB, PT, SB, ST, HB, HT,
         LB, L1, L2, L3, L4, L5, L6, PC };

   int codigo[1024];
   int pila[1024];
   int registros[16];
%}

%union {
       long valor;         /* Constant */
}

%token <valor> NUM

/*
 *  precedence table
 */
/*%right    '=' PE ME TE DE RE
%left     '|'
%left     '^'
%left     '&'
%left     EQ NE
%left     '<' '>' GE LE
%left     '+' '-'
%left     '*' '/' '%'
%right    PP MM*/


/*
 * typed non-terminal symbols 
 */
//%type <ptr> type optionalParameterList parameterList binary

%%
program      : instructions
             ;

instructions : instruction
             | instructions instruction       { yyerrok;}
             | error
             | instructions error
             ;

instruction  : NUM NUM NUM NUM '\n' { codigo[registros[CT]++] = $1;
                                      codigo[registros[CT]++] = $2;
                                      codigo[registros[CT]++] = $3;
                                      codigo[registros[CT]++] = $4;
                                    }
             ;



%%

/*
 *  function main
 */

#include <string.h>
#include <stdio.h>


//directivas
void inc(){
   pila[registros[ST]]++;
}

void mult(){
   pila[registros[ST]-1] *= pila[registros[ST]];
   registros[ST]--;
}

void get(){
   scanf("%d",&pila[pila[registros[ST]--]]);
}

void put(){
   fprintf(stderr,"%d",pila[registros[ST]--]);
}





//carga un objeto de n palabras desde la dirección (d + registro r) y lo empuja en la pila
void load(int n,int d, int r){
   int i;
   for (i=0;i<n;i++){
      pila[++registros[ST]] = pila[registros[r]+d+i];
   }
}

//empuja la direccion del dato(d + registro r) en la pila
void loada(int d,int r){
   pila[++registros[ST]] = registros[r]+d;
}

//saca una direccion de la pila, carga un objeto de n palabras desde esa direccion y lo empuja en la pila
void loadi(n){
   int dir = pila[registros[ST]--];
   int i;
   for(i = 0;i<n;i++){
      pila[++registros[ST]] = pila[dir+i];
   }
}

//empuja en la pila el literal d (1 palabra)
void loadl(int d){
   pila[++registros[ST]] = d;
}

//saca un objeto de tamaño n de la pila y lo almacena en la direccion (d + registro r)
void store(int n,int d, int r){
   int i;
   for(i = n-1;i>=0;i--){
      pila[registros[r]+d+i] = pila[registros[ST]--];
   }
}

//saca un objeto de tamaño n de la pila, entonces, saca una direccion y almacena el objeto en la direccion
void storei(int n){
   int dir = pila[registros[ST]-n];
   int i;
   for(i = n-1;i>=0;i--){
      pila[dir] = pila[registros[ST]--];
   }
   registros[ST]--; //elimina la dirección
}

//llama a la rutina cuya direccion es(d + registro r), usando la direccion en el registro n como enlace estatico
void call(int n,int d, int r){
   if(r != PB){
      loadl(registros[LB]);   //empuja el enlace dinamico
      registros[LB] = registros[ST]; //el nuevo registro de activación empieza aqui
      loadl(registros[PC]+4); //empuja PC+4 actual que es la direccion de retorno
      registros[PC] = registros[r]+d; //salta a la rutina
      registros[PC]-= 4; //para arreglar que en el bucle se incremente
   }
   else{
      switch(d){
         case 5:
            inc();
         break;
         case 10:
            mult();
         break;
         case 23:
            get();
         break;
         case 24:
            put();
         break;
         default:
            fprintf(stderr,"codigo de directiva erronea\n\n");
            exit(1);
         break;
      }
   }
}

//saca de la pila el enlace estatico y la direccion de comienzo de la rutina y la llama
void calli(){
   fprintf(stderr,"not yet implemented\n\n");
   exit(1);
}

//retorna de la rutina actual: saca de la pila un resultado de tamaño n, elimina el registro de activacion del tope,elimina las d palabras de los parametros, y entonces empuja el resultado a devolver en la pila
void Return(int n,int d){
   int dirret = pila[registros[LB]+1];  //salvaguarda la direccion de retorno
   int enldin = pila[registros[LB]];    //salvaguarda el enlace dinamico
   int i;
   for(i = 0;i<n;i++){
      pila[registros[LB]-d+i] = pila[registros[ST]-n+i+1];
   }
   registros[ST] = registros[LB]-d+n-1;
   registros[PC] = dirret;
   registros[LB] = enldin;
   registros[PC]-= 4; //para arreglar que en el bucle se incremente
}

//empuja d palabras sin inicializar en la pila
void push(int d){
   registros[ST]+=d;
}

//saca un resultado de n palabras de la pila, a continuacion, saca d palabras mas de la misma, y entonces empuja el resultado de nuevo en la pila
void pop(int n,int d){
   int i;
   for(i=0;i<n;i++){
      pila[registros[ST]-d+i] = pila[registros[ST]+i];
   }
   registros[ST]-=d;
}

//salta a la direccion de codigo (d + registro r)
void jump(int d,int r){
   registros[PC] = registros[r]+d;
   registros[PC]-= 4; //para arreglar que en el bucle se incremente
}

//saca de la pila una direccion de codigo y salta a la misma
void jumpi(){
   registros[PC] = registros[ST]--;
   registros[PC]-= 4; //para arreglar que en el bucle se incremente
}

//saca una palabra de la pila y salta a la direccion de codigo (d + registro r) si y solo si, ese valor es igual a n
void jumpif(int n,int d, int r){
   int valor = pila[registros[ST]--];
   if(valor == n){
      jump(d,r);
      registros[PC]-= 4; //para arreglar que en el bucle se incremente
   }
}

//detiene la ejecucion del programa
void halt(){
   exit(0);
}

void imprime_pila(){
   int i;
   fprintf(stderr,"pila:[");
   for(i = 0;i<=registros[ST];i++){
      fprintf(stderr,"%d, ",pila[i]);
   }
   fprintf(stderr,"]\n");
}

main( int argc, char ** argv ){

  #ifdef DEBUG
    extern int yydebug;
  #endif

  extern FILE *yyin, *yyout;
  char *dest, *errf, *c;

  #ifdef DEBUG
    yydebug = 1;
  #endif

  dest = (char *) malloc(255);
  errf = (char *) malloc(255);

  if ( argc > 1 )
    strcpy( dest , argv[1]);
  else
  {
    printf("\nIntroduzca el nombre del Fichero Sample C: ");
    scanf("%s", dest);
  }

  yyin = fopen( dest, "r");
  if (!yyin)
  {
    fprintf( stderr, "no se pudo abrir el fichero %s\n", argv[1] );
    exit(1);
  }

  strcpy( errf , dest);
  c = strchr( errf, '.');
  strcpy(c, ".err");

  yyerfp = fopen( errf, "w");
  if (!yyerfp)
  {
    fprintf( stderr, "no se pudo abrir el fichero %s\n", errf );
    exit(1);
  }


  yyparse();
  fclose(yyin);
  fclose(yyout);
  fclose(yyerfp);

  if (yynerrs != 0)
    fprintf( stderr, "\n!!!!Errores  en el fichero %s!!!!!!!\n", errf);
  else
    fprintf( stderr, "\n!!!!Compilaci�n Correcta del fichero %s!!!!!!!\n", dest);

   fprintf(stderr,"%d\n",registros[CT]);




   registros[ST] = -1;



   while(1){
      int op   = codigo[registros[PC]];
      int r = codigo[registros[PC]+1];
      int n = codigo[registros[PC]+2];
      int d = codigo[registros[PC]+3];
      if(registros[PC]<0 || registros[PC]>= registros[CT]){
         fprintf(stderr,"ejecucion fuera del codigo (segmentation fault?)\n\n");
         exit(1);
      }
      imprime_pila();
      fprintf(stderr,"%d %d %d %d\n",op,r,n,d);
      switch(op){
         case LOAD:
            load(n,d,r);
         break;
         case LOADA:
            loada(d,r);
         break;
         case LOADI:
            loadi(n);
         break;
         case LOADL:
            loadl(d);
         break;
         case STORE:
            store(n,d,r);
         break;
         case STOREI:
            storei(n);
         break;
         case CALL:
            call(n,d,r);
         break;
         case CALLI:
            calli();
         break;
         case RETURN:
            Return(n,d);
         break;
         case PUSH:
            push(d);
         break;
         case POP:
            pop(n,d);
         break;
         case JUMP:
            jump(d,r);
         break;
         case JUMPI:
            jumpi();
         break;
         case JUMPIF:
            jumpif(n,d,r);
         break;
         case HALT:
            halt();
         break;
         default:
            fprintf(stderr,"opcode error\n\n");
            exit(1);
         break;
      }
      registros[PC]+=4;
      getchar();
   }//bucle infinito
} /* main */
